package service

import (
	"context"
	"errors"
	"gorm.io/gorm"
	"shop-bean/constant"
	"shop-bean/model"
	"shop-bean/request"
	"shop-common/library/variables"
	"shop-common/utils"
	"shop-common/utils/pagehelper"
	"shop-service/cache"
	"shop-service/repository"
)

type INoticeService interface {
	FindPage(ctx context.Context, paginator *pagehelper.Paginator) (*pagehelper.Record, error)
	FindById(ctx context.Context, id uint) (notice *model.Notice, err error)
	Crate(ctx context.Context, notice *model.Notice, fileIds []uint) error
	Updates(ctx context.Context, req *request.NoticeRequest) error
	DeleteBatch(ctx context.Context, ids []uint) error
	FindTopNotice(ctx context.Context) ([]*model.Notice, error)
}

type NoticeService struct {
	db                *gorm.DB
	repository        repository.INoticeRepository
	attachFileService IAttachFileService
	redisClient       cache.IRedisNoticeCache
}

func NewNoticeService(attachFileService IAttachFileService,
	repository repository.INoticeRepository) INoticeService {
	return &NoticeService{variables.GormDB(), repository, attachFileService, cache.NewRedisNoticeCache()}
}

func (s *NoticeService) Crate(ctx context.Context, notice *model.Notice, fileIds []uint) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		if err := s.repository.Crate(tx, notice); err != nil {
			variables.Logger.Errorf(ctx, "create notice failed: err %v, %+v", err, notice)
			return err
		}

		if len(fileIds) > 0 {
			if err := s.attachFileService.UpdateBatchAttachFileStatus(ctx, tx, fileIds, constant.FILE_NORMAL); err != nil {
				return err
			}
		}

		_ = s.redisClient.RemoveTopListCache()
		return nil
	})
}

func (s *NoticeService) Updates(ctx context.Context, req *request.NoticeRequest) error {
	var notice *model.Notice
	utils.StructMap(req, &notice)
	return s.db.Transaction(func(tx *gorm.DB) error {
		if err := s.repository.Updates(tx, notice); err != nil {
			variables.Logger.Errorf(ctx, "updates notice failed: err %v, notice %+v", err, notice)
			return err
		}

		if len(req.FileIds) > 0 {
			if err := s.attachFileService.UpdateBatchAttachFileStatus(ctx, tx, req.FileIds, constant.FILE_NORMAL); err != nil {
				return err
			}
		}

		//修改过期图片状态
		if len(req.ExpireFileIds) > 0 {
			if err := s.attachFileService.UpdateBatchAttachFileStatus(ctx, tx, req.ExpireFileIds, constant.FILE_OVERDUE); err != nil {
				return err
			}
		}

		_ = s.redisClient.RemoveTopListCache()
		_ = s.redisClient.EvictNoticeById(req.Id)

		return nil
	})
}

func (s *NoticeService) FindPage(ctx context.Context, paginator *pagehelper.Paginator) (*pagehelper.Record, error) {
	count, err := s.repository.Count(s.db, paginator.Params)
	if err != nil {
		variables.Logger.Errorf(ctx, "find notice to count failed: err %v, params %+v", err, paginator.Params)
		return paginator.Record, err
	}

	if count == 0 {
		return paginator.Record, nil
	}

	page, err := s.repository.FindPage(s.db, paginator.Params)
	if err != nil {
		variables.Logger.Errorf(ctx, "find notice to page: err: %v, params %+v", err, paginator.Params)
		return paginator.Record, err
	}

	paginator.Count(count).Record.List = page
	return paginator.Record, nil
}

func (s *NoticeService) FindById(ctx context.Context, id uint) (notice *model.Notice, err error) {
	notice, err = s.redisClient.GetNoticeById(id)
	if notice != nil && err == nil {
		return
	}

	notice, err = s.repository.FindById(s.db, id)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		variables.Logger.Errorf(ctx, "findById notice failed: err %v, id %d", err, id)
		return nil, err
	}

	err = s.redisClient.PutNoticeById(id, notice)
	if err != nil {
		variables.Logger.Errorf(ctx, "cache set by id notice failed: err %v", err)
	}

	return notice, nil
}

func (s *NoticeService) DeleteBatch(ctx context.Context, ids []uint) error {
	return s.db.Transaction(func(tx *gorm.DB) error {
		if err := s.repository.DeleteBatch(tx, ids); err != nil {
			variables.Logger.Errorf(ctx, "deleteBatch notice failed: err %v, ids %s", err, ids)
			return err
		}

		// todo 异步方法  sync.WaitGroup
		_ = s.redisClient.RemoveTopListCache()
		for _, id := range ids {
			_ = s.redisClient.EvictNoticeById(id)
		}

		return nil
	})
}

func (s *NoticeService) FindTopNotice(ctx context.Context) ([]*model.Notice, error) {
	list, err := s.redisClient.GetTopListCache()
	if len(list) > 0 && err == nil {
		return list, nil
	}

	notices, err := s.repository.FindTopList(s.db)
	if err != nil {
		variables.Logger.Errorf(ctx, "find notice failed: err %v", err)
		return nil, err
	}

	err = s.redisClient.PutTopListCache(notices)
	if err != nil {
		variables.Logger.Errorf(ctx, "cache set notices failed: err %v", err)
	}

	return notices, err
}
